package cc.aliza.lib.mongo.core.utils;

import cc.aliza.lib.mongo.core.AlizaEntity;
import cc.aliza.lib.mongo.core.annotations.Embed;
import cc.aliza.lib.mongo.core.annotations.EmbedList;
import cc.aliza.lib.mongo.core.annotations.Ref;
import cc.aliza.lib.mongo.core.annotations.RefList;
import com.mongodb.DBRef;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Optional;

/**
 * 『给时光以生命·给岁月以文明』
 *
 * @author Zhang Jing (byrdkm17@gmail.com).
 */
public final class FieldUtil {

    private static final Logger logger = LoggerFactory.getLogger(FieldUtil.class);

    public static Object get(Object obj, Field field) {
        Object value = null;
        try {
            value = field.get(obj);
        } catch (IllegalAccessException e) {
            logger.error("Can not get field's value", e);
        }
        return value;
    }

    public static void set(Object obj, Field field, Object value) {
        try {
            field.set(obj, value);
        } catch (IllegalAccessException e) {
            logger.error("Can not set field's value", e);
        }
    }

    public static Class<?> getRealType(Field field, Object value) {
        return getRealType(field, field.getType(), value);
    }

    public static Class<?> getRealType(Field field, Class<?> clazz, Object value) {
        if (clazz.isInterface()) {
            Ref ref = field.getAnnotation(Ref.class);
            if (ref != null) {
                return getRealType(ref.impl(), (AlizaEntity) value, clazz);
            }
            RefList refList = field.getAnnotation(RefList.class);
            if (refList != null) {
                if (value instanceof DBRef) {
                    return getRealType(refList.impl(), (DBRef) value, clazz);
                } else {
                    return getRealType(refList.impl(), (AlizaEntity) value, clazz);
                }
            }
            Embed embed = field.getAnnotation(Embed.class);
            if (embed != null) {
                return getRealType(embed.impl(), ((Document) value).getString("_impl"), clazz);
            }
            EmbedList embedList = field.getAnnotation(EmbedList.class);
            if (embedList != null) {
                return getRealType(embedList.impl(), ((Document) value).getString("_impl"), clazz);
            }
        }
        return clazz;
    }

    private static Class<?> getRealType(Class<?>[] clazz, String impl, Class<?> defaultClass) {
        if (StringUtil.isEmpty(impl)) {
            return defaultClass;
        }
        Optional<Class<?>> optional = Arrays.stream(clazz).filter(o -> impl.equals(o.getSimpleName())).findFirst();
        return optional.isPresent() ? optional.get() : defaultClass;
    }

    private static Class<?> getRealType(Class<?>[] clazz, AlizaEntity entity, Class<?> defaultClass) {
        Optional<Class<?>> optional = Arrays.stream(clazz).filter(o -> MapperUtil.getDBName(entity.getClass()).equals(MapperUtil.getDBName(o))).findFirst();
        return optional.isPresent() ? optional.get() : defaultClass;
    }

    private static Class<?> getRealType(Class<?>[] clazz, DBRef ref, Class<?> defaultClass) {
        Optional<Class<?>> optional = Arrays.stream(clazz).filter(o -> ref.getCollectionName().equals(MapperUtil.getDBName(o))).findFirst();
        return optional.isPresent() ? optional.get() : defaultClass;
    }

}
